/*
*
* Copyright (c) 2011 Vaulting Systems International
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy 
* of this software and associated documentation files (the "Software"), to deal 
* in the Software without restriction, including without limitation the rights 
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
* of the Software, and to permit persons to whom the Software is furnished to do  
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all  
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE  
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.ekeyman.securesavelib.business;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.PutObjectResult;
import com.amazonaws.services.s3.model.S3Object;
import com.ekeyman.securesave.service.EkeymanService;
import com.ekeyman.securesave.utils.DigitalSignature;
import com.ekeyman.securesavelib.dao.AccessAuditDao;
import com.ekeyman.securesavelib.dao.RegistryDao;
import com.ekeyman.securesavelib.dao.ResourceDao;
import com.ekeyman.securesavelib.domain.AccessAudit;
import com.ekeyman.securesavelib.domain.Registry;
import com.ekeyman.securesavelib.domain.Resource;
import com.ekeyman.securesavelib.domain.User;
import com.ekeyman.securesavelib.dto.EncryptionKeys;
import com.ekeyman.securesavelib.dto.FileDownloadResponse;
import com.ekeyman.securesavelib.dto.ResourceGrid;
import com.ekeyman.securesavelib.dto.SearchFilter;
import com.ekeyman.securesavelib.util.SecureSaveUtils;

public class ResourceBO {
	
	private ResourceDao resourceDao;
	private AccessAuditDao accessAuditDao;
	private RegistryDao registryDao;
	private EkeymanService ekeymanService;
	
	public ResourceBO(){}
	
	public FileDownloadResponse processDownloadFile(long resourceId, User user, String passphrase) throws Exception{	
		
		FileDownloadResponse fileDownloadResponse = null;
	
		try{
			Registry registry = registryDao.getRegistry();
			if(registry != null){
				Resource resource  = getResourceDao().getResourceById(resourceId);
				
				if(resource != null){
					
					if(resource.getUser().getOpenidurl().equals(user.getOpenidurl())){
					
						EncryptionKeys encryptionKeys = ekeymanService.getEncryptionKeys(
								resource.getResourceUri(), 
								registry.getPublicKey(), 
								registry.getPrivateKey(), 
								"App Server");
		
					    
						AWSCredentials awsCredentials = 
							new BasicAWSCredentials(registry.getAccessKeyId(), registry.getSecretAccessKey());
						AmazonS3 s3 = new AmazonS3Client(awsCredentials);
		
						S3Object s3Object = s3.getObject(registry.getBucketUri(), resource.getResourceUri());
						InputStream s3is = s3Object.getObjectContent();
						
					    String tempDirectory = SecureSaveUtils.getTemporaryDirectory();
					    File decryptedFile = new File(tempDirectory + File.separator + resource.getResourceUri());

						if(passphrase == null || passphrase.equalsIgnoreCase("")){
							SecureSaveUtils.decrypt(encryptionKeys, s3is, decryptedFile);
						}
						else{
							SecureSaveUtils.decrypt(encryptionKeys, s3is, decryptedFile, passphrase);							
						}
				
						String signature = DigitalSignature.createDigitalSignature(decryptedFile);
						if(resource.getFileSignature().equals(signature)){
				            InputStream downloadedFis  = new FileInputStream(decryptedFile);
				            fileDownloadResponse = new FileDownloadResponse();
				            fileDownloadResponse.setFilename(resource.getFilename());
				            fileDownloadResponse.setFiletype(resource.getFiletype());
				            fileDownloadResponse.setFileInputStream(downloadedFis);
						}
						else {
							// Digital signature of uploaded files does not match 
							// digital signature of downloaded file. If this happens then
							// the file must have been tampered with...DO NOT DOWNLOAD!
						}
					}
					else {
						// Security violation! Logged in user openidurl does not match the
						// openidurl of the person who saved the file...DO NOT DOWNLOAD!
						// The resourceId is contained on a link in the grid. A hacker can
						// easily copy the URL and change the resourceId and submit the link.
					}
				}
			}
		}
		catch(Exception e){
			// Error handling can be improved. 
			throw e;
		}
		
		return fileDownloadResponse;
	}
	
	
	public void processUploadedFile(File uploadedFile, String filename, String filetype, User user, String passphrase, String fileDescription) throws Exception{
		/*
		 * I don't know why, but changing upload file size in struts.xml kept giving
		 * a null value for the uploadedFile variable. So the following was successfully
		 * changed in struts.properties:
		 * 
		 *     struts.multipart.maxSize=1073741824
		 *     
		 */
		PutObjectResult putObjectResult = null;
		Registry registry = null;
		File encryptedFile = null;
		String resourceUri = null;
		
		try {
			registry = registryDao.getRegistry();
			
			if(registry != null){
				
				 // The resourceUri must ALWAYS be unique for each file. Do not use the file name!
				resourceUri = SecureSaveUtils.getUniqueIdentifier() + ".bin";
				
				String tempDirectory = SecureSaveUtils.getTemporaryDirectory();  
				
				String resourceUriEncrypted = tempDirectory + File.separator + resourceUri;
				encryptedFile = new File(resourceUriEncrypted);
				
				EncryptionKeys encryptionKeys = ekeymanService.createEncryptionKeys(
						resourceUri.toString(), 
						registry.getPublicKey(), 
						registry.getPrivateKey(), 
						16, 
						16, 
						16, 
						1024, 
						"App Server");
		
				Resource resource = new Resource();
				
				if(passphrase == null || passphrase.equalsIgnoreCase("")){
					SecureSaveUtils.encrypt(encryptionKeys, uploadedFile, encryptedFile);
					resource.setPassphraseProtected("N");
				}
				else {
					SecureSaveUtils.encrypt(encryptionKeys, uploadedFile, encryptedFile, passphrase);
					resource.setPassphraseProtected("Y");
				}
				
				AWSCredentials awsCredentials = 
					new BasicAWSCredentials(registry.getAccessKeyId(), registry.getSecretAccessKey());
				AmazonS3 s3 = new AmazonS3Client(awsCredentials);
	
				putObjectResult = s3.putObject(registry.getBucketUri(), resourceUri, encryptedFile);
				
				resource.setFileSignature(DigitalSignature.createDigitalSignature(uploadedFile));
				resource.setResourceUri(resourceUri);
				resource.setBucketUri(registry.getBucketUri());
				resource.setFilename(filename);
				resource.setFiletype(filetype);
				resource.setFileDescription(fileDescription);
				resource.setFilelength(uploadedFile.length());
				resource.setUser(user);
				getResourceDao().saveResource(resource);
	
				AccessAudit accessAudit = new AccessAudit();
				accessAudit.setAccessResult(AccessAudit.SUCCESS);
				accessAudit.setAccessType("create resource");
				accessAudit.setResourceUri(resourceUri);
				getAccessAuditDao().saveAccessAudit(accessAudit);
				
			}
		}
		catch(Exception e){
			// If an exception occurred after saving the file to the bucket
			// it must be deleted
			if(putObjectResult != null){
				if(registry != null){
					AWSCredentials awsCredentials = 
						new BasicAWSCredentials(registry.getAccessKeyId(), registry.getSecretAccessKey());
					AmazonS3 s3 = new AmazonS3Client(awsCredentials);
		
					s3.deleteObject(registry.getBucketUri(), resourceUri);
				}
			}
			throw e;
		}
		finally {
			if(uploadedFile != null){
				uploadedFile.delete();	
			}
			if(encryptedFile != null){
				encryptedFile.delete();
			}
		}
	}

	public void createResource(String resourceUri, 
			String bucketUri, String filename, String filetype, int filelength, User user) {
		
		Resource resource = new Resource();
		resource.setResourceUri(resourceUri);
		resource.setBucketUri(bucketUri);
		resource.setFilename(filename);
		resource.setFiletype(filetype);
		resource.setFilelength(filelength);
		resource.setUser(user);
		getResourceDao().saveResource(resource);

		AccessAudit accessAudit = new AccessAudit();
		accessAudit.setAccessResult(AccessAudit.SUCCESS);
		accessAudit.setAccessType("create resource");
		accessAudit.setResourceUri(resourceUri);
		getAccessAuditDao().saveAccessAudit(accessAudit);
	}
	
	public Resource getResource(String resourceUri, 
			String bucketUri, String filename){
		
		return getResourceDao().getResource(resourceUri, bucketUri, filename);
	}

	public void deleteResourceById(long id, User user){
		
		Registry registry = registryDao.getRegistry();
		if(registry != null){

			Resource resource = getResourceDao().getResourceById(id);
			if(resource != null){
				
				if(resource.getUser().getOpenidurl().equalsIgnoreCase(user.getOpenidurl())){
					getResourceDao().deleteResourceById(id);
					
					AWSCredentials awsCredentials = 
						new BasicAWSCredentials(registry.getAccessKeyId(), registry.getSecretAccessKey());
					AmazonS3 s3 = new AmazonS3Client(awsCredentials);
	
					s3.deleteObject(registry.getBucketUri(), resource.getResourceUri());
					
					getEkeymanService().deleteEncryptionKeys(
							resource.getResourceUri(), 
							registry.getPublicKey(), 
							registry.getPrivateKey(), 
							"App Server");
					
					AccessAudit accessAudit = new AccessAudit();
					accessAudit.setAccessResult(AccessAudit.SUCCESS);
					accessAudit.setAccessType("delete resource");
					accessAudit.setResourceUri(resource.getResourceUri());
					getAccessAuditDao().saveAccessAudit(accessAudit);
					
				}
				else{
					// Security violation
				}
			}
		}
	}
	
	public long getResourceCount(List<SearchFilter> searchFilters){
		return getResourceDao().getResourceCount(searchFilters);
	}

	public List<ResourceGrid> findResources(List<SearchFilter> searchFilters, 
			String sortIndex, String sortOrder, int firstResult, int maxResults){

		List<Resource> resources = getResourceDao().findResources(searchFilters, sortIndex, sortOrder, firstResult, maxResults);	
		return gridTransformation(resources);
	}
	
	private List<ResourceGrid> gridTransformation(List<Resource> resources){
		
		List<ResourceGrid> gridResources = new ArrayList<ResourceGrid>();
		
		for(Resource rs : resources){
			ResourceGrid rdg = new ResourceGrid();
			rdg.setId(rs.getId());
			rdg.setResourceUri(rs.getResourceUri());
			rdg.setFilename(rs.getFilename());
			rdg.setFiletype(rs.getFiletype());
			rdg.setFileDescription(rs.getFileDescription());
			rdg.setPassphraseProtected(rs.getPassphraseProtected());
			rdg.setFilelength(rs.getFilelength());
			rdg.setBucketUri(rs.getBucketUri());
			rdg.setLink("Download");
			gridResources.add(rdg);
		}
		
		return gridResources;
	}

	public void setResourceDao(ResourceDao resourceDao) {
		this.resourceDao = resourceDao;
	}

	public ResourceDao getResourceDao() {
		return resourceDao;
	}

	public AccessAuditDao getAccessAuditDao() {
		return accessAuditDao;
	}

	public void setAccessAuditDao(AccessAuditDao accessAuditDao) {
		this.accessAuditDao = accessAuditDao;
	}

	public void setRegistryDao(RegistryDao registryDao) {
		this.registryDao = registryDao;
	}

	public RegistryDao getRegistryDao() {
		return registryDao;
	}

	public void setEkeymanService(EkeymanService ekeymanService) {
		this.ekeymanService = ekeymanService;
	}

	public EkeymanService getEkeymanService() {
		return ekeymanService;
	}
}
